package model;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.List;
import java.util.Set;
import java.util.HashSet;
import java.util.HashMap;
import java.util.EnumMap;
import java.util.ArrayList;
import java.util.Collections;

import _bye_util.IntRecycler;
import _bye_util.IntRecyclerFactory;

/**
 * This class handles the execution on the command of its units.
 * 
 * /Player/ -- -standbySet: Set<Unit> -explorerSet: Set<Explorer>
 * -instanceToLocation: Map<ModelInstance, Location> -instanceToUnit:
 * Map<ModelInstance, Unit> -instanceToRallyPoint: Map<ModelInstance,
 * RallyPoint> -instanceToStructure: Map<ModelInstance, Structure>
 * -unitToRallyPoint: Map<Unit, RallyPoint> -StructureToBase: Map<Structure,
 * Base> -locationToUnits: Map<Location, List<Unit> > -locationToRallyPoints:
 * Map<Location, List<RallyPoint> > -locationToStructure: Map<Location,
 * Structure> -idToRallyPoint: Map<Integer, RallyPoint> -- +harvest(instance:
 * ModelInstance): void +powerUp(instance: ModelInstance): void
 * +powerDown(instance: ModelInstance): void +disband(instance: ModelInstance):
 * void +decommission(instance: ModelInstance): void
 * +createRallyPoint(rallypoint: int): void +leaveArmy(instance: ModelInstance):
 * void +build(structure: Structure, builder: ModelInstance): void
 * +buildBase(base: Base, builder: ModelInstance): void +train(unit: Unit,
 * trainer: ModelInstance +trainExplorer(explorer: Explorer, trainer:
 * ModelInstance +heal(instance: ModelInstance): void +joinRallyPoint(instance:
 * ModelInstance, id: int): void +attack(instance: ModelInstance, direction:
 * DirectionEnum): void +defend(instance: ModelInstance, direction:
 * DirectionEnum): void +move(instance: ModelInstance, command: Move): void
 * +ownsLocation(location: Location): boolean +hasLostTheGame(): boolean
 * +onTick(enemy: Player): void
 * 
 * @author alex kagioglu
 * @author ryan
 */
abstract class Player implements NotPartOfTheSystem
{
    // -- Attributes --//
    
    // -map: Map
    private GameMap map;
    
    // -enemyPlayer: Player
    private Player enemy;
    
    // -standbySet: Set<Unit>
    private HashSet< Unit > standbySet;
    
    // -meleeIntRecycler: ConstantLeakyRecycler
    private IntRecycler meleeIntRecycler;
    
    // -meleeIntRecycler: ConstantLeakyRecycler
    private IntRecycler rangedIntRecycler;
    
    // -meleeIntRecycler: ConstantLeakyRecycler
    private IntRecycler colonistIntRecycler;
    
    // -meleeIntRecycler: ConstantLeakyRecycler
    private IntRecycler explorerIntRecycler;
    
    // -meleeIntRecycler: ConstantLeakyRecycler
    private IntRecycler baseIntRecycler;
    
    // -meleeIntRecycler: ConstantLeakyRecycler
    private IntRecycler towerIntRecycler;
    
    // -rallyPointSet: Set<RallyPoint>
    // should have been in human. i moved it. alex kagioglu
    
    // -structureSet: Set<Structure>
    // should have been in human. i moved it. alex kagioglu
    
    // -explorerSet: Set<Explorer>
    private Map< Unit, Explorer > explorerMap;
    
    // Added: Map of all colonists owned by the player
    private Map< Unit, Colonist > colonistMap;
    
    // Added: Map of all melee units owned by the player
    private Map< Unit, Melee > meleeMap;
    
    // Added: Map of all ranged units owned by the player
    private Map< Unit, Ranged > rangedMap;
    
    // Added: Map of all towers owned by the player
    private Map< Structure, Tower > towerMap;
    
    // Added: Map of all bases owned by the player
    private Map< Structure, Base > baseMap;
    
    // -instanceToLocation: Map<ModelInstance, Location>
    private Map< ModelInstance, Location > instanceToLocation;
    
    // -instanceToUnit: Map<ModelInstance, Unit>
    private Map< ModelInstance, Unit > instanceToUnit;
    
    // -instanceToRallyPoint: Map<ModelInstance, RallyPoint>
    private Map< ModelInstance, RallyPoint > instanceToRallyPoint;
    
    // -instanceToStructure: Map<ModelInstance, Structure>
    private Map< ModelInstance, Structure > instanceToStructure;
    
    // -unitToRallyPoint: Map<Unit, RallyPoint>
    private Map< Unit, RallyPoint > unitToRallyPoint;
    
    // -StructureToBase: Map<Structure, Base>
    private Map< Structure, Base > structureToBase;
    
    // -locationToUnits: Map<Location, List<Unit> >
    private Map< Location, List< Unit >> locationToUnits;
    
    // -locationToRallyPoints: Map<Location, List<RallyPoint> >
    private Map< Location, List< RallyPoint >> locationToRallyPoint;
    
    // -locationToStructure: Map<Location, Structure>
    private Map< Location, Structure > locationToStructure;
    
    // -idToRallyPoint: Map<Integer, RallyPoint>
    private Map< Integer, RallyPoint > idToRallyPoint;
    
    // keep track of resources
    private Map< ResourceType, Integer > stock;
    
    // DEVIATION: Need a set of DecalTypes for the player to know how to view
    // the map
    private Map< Location, DecalType > visibleMap;
    
    // Keeps track of the cost to build a type of instance
    // Need this so that we know how much a unit or structure will cost before
    // we instantiate it.
    private Map< InstanceType, Map< ResourceType, Integer >> instanceCosts;
    
    // The cap on the number of each type of unit.
    private static final int typeCap = 10;
    
    // Keeps track of the number of units the player has of each type.
    private int numMelee;
    private int numRanged;
    private int numColonist;
    private int numExplorer;
    private int numBase;
    private int numTower;
    
    // The total number of all units of all types combined the player may have.
    private static final int totalUnitCap = 25;
    
    // The total number of structures of all types combined the player may have.
    private static final int totalStructCap = 25;
    
    private Location startingLocation;
    
    // -- Constructor -- //
    
    /**
     * Creates a new player with a given GameMap, starting point on that map,
     * and starting resources.
     * 
     * The player starts with 2 explorers and 1 colonist on the starting tile,
     * but no other units.
     * 
     * @author Christopher Dudley
     * @author more!
     * 
     * @param map
     *            the map the player is on.
     * @param startPoint
     *            the starting point on the map for the player.
     * @param startResources
     *            the resources the player starts with.
     * @param startUnits
     *            the list of units (as types) that the player starts with.
     * @param startStructure
     *            the structure (as a type) the player starts with.
     * @param typeCaps
     *            a map of each instance type to the maximum number of that type
     *            the player can have.
     * @param unitCap
     *            the maximum total number of units the player can have.
     * @param structCap
     *            the maximum total number of structures the player can have.
     */
    public Player( GameMap map, Location startLoc, Map< ResourceType, Integer > startResources ) {
        meleeIntRecycler = IntRecyclerFactory.make();
        rangedIntRecycler = IntRecyclerFactory.make();
        colonistIntRecycler = IntRecyclerFactory.make();
        explorerIntRecycler = IntRecyclerFactory.make();
        baseIntRecycler = IntRecyclerFactory.make();
        towerIntRecycler = IntRecyclerFactory.make();
        
        startingLocation = startLoc;
        
        enemy = null;
        standbySet = new HashSet< Unit >();
        instanceToLocation = new HashMap< ModelInstance, Location >();
        instanceToUnit = new HashMap< ModelInstance, Unit >();
        instanceToRallyPoint = new HashMap< ModelInstance, RallyPoint >();
        instanceToStructure = new HashMap< ModelInstance, Structure >();
        unitToRallyPoint = new HashMap< Unit, RallyPoint >();
        structureToBase = new HashMap< Structure, Base >();
        locationToUnits = new HashMap< Location, List< Unit >>();
        locationToRallyPoint = new HashMap< Location, List< RallyPoint >>();
        locationToStructure = new HashMap< Location, Structure >();
        idToRallyPoint = new HashMap< Integer, RallyPoint >();
        visibleMap = new HashMap< Location, DecalType >();
        
        this.map = map;
        stock = startResources;
        
        stock.put( ResourceType.NONE, 0 );
        
        // Initialize sets of all unit types.
        explorerMap = new HashMap< Unit, Explorer >();
        colonistMap = new HashMap< Unit, Colonist >();
        meleeMap = new HashMap< Unit, Melee >();
        rangedMap = new HashMap< Unit, Ranged >();
        baseMap = new HashMap< Structure, Base >();
        towerMap = new HashMap< Structure, Tower >();
        
        // Player starts with 2 explorers, 1 colonist and 1 base.
        // init(startLoc);
        
        // Hard code costs of units and structures
        
        instanceCosts = new EnumMap< InstanceType, Map< ResourceType, Integer >>(
                InstanceType.class );
        
        Map< ResourceType, Integer > colonistCost = new EnumMap< ResourceType, Integer >(
                ResourceType.class );
        colonistCost.put( ResourceType.RED, 0 );
        colonistCost.put( ResourceType.GREEN, 0 );
        colonistCost.put( ResourceType.BLUE, 10 );
        instanceCosts.put( InstanceType.COLONIST, colonistCost );
        
        Map< ResourceType, Integer > meleeCost = new EnumMap< ResourceType, Integer >(
                ResourceType.class );
        meleeCost.put( ResourceType.RED, 15 );
        meleeCost.put( ResourceType.GREEN, 0 );
        meleeCost.put( ResourceType.BLUE, 0 );
        instanceCosts.put( InstanceType.MELEE, meleeCost );
        
        Map< ResourceType, Integer > rangedCost = new EnumMap< ResourceType, Integer >(
                ResourceType.class );
        rangedCost.put( ResourceType.RED, 10 );
        rangedCost.put( ResourceType.GREEN, 5 );
        rangedCost.put( ResourceType.BLUE, 0 );
        instanceCosts.put( InstanceType.RANGED, rangedCost );
        
        Map< ResourceType, Integer > explorerCost = new EnumMap< ResourceType, Integer >(
                ResourceType.class );
        explorerCost.put( ResourceType.RED, 0 );
        explorerCost.put( ResourceType.GREEN, 5 );
        explorerCost.put( ResourceType.BLUE, 5 );
        instanceCosts.put( InstanceType.EXPLORER, explorerCost );
        
        Map< ResourceType, Integer > baseCost = new EnumMap< ResourceType, Integer >(
                ResourceType.class );
        baseCost.put( ResourceType.RED, 10 );
        baseCost.put( ResourceType.GREEN, 10 );
        baseCost.put( ResourceType.BLUE, 10 );
        instanceCosts.put( InstanceType.BASE, baseCost );
        
        Map< ResourceType, Integer > towerCost = new EnumMap< ResourceType, Integer >(
                ResourceType.class );
        towerCost.put( ResourceType.RED, 5 );
        towerCost.put( ResourceType.GREEN, 10 );
        towerCost.put( ResourceType.BLUE, 0 );
        instanceCosts.put( InstanceType.TOWER, towerCost );
        
        visibleMap.put( startLoc, DecalType.EXPLORED_BASIC );
    }
    
    // -- Methods --//
    
    protected void initialUnits( Location startLoc )
    {
        Explorer e1 = new Explorer( explorerIntRecycler );
        
        // Explorers must be added to the explorer set
        explorerMap.put( e1, e1 );
        
        addNewUnit( e1, startLoc );
        
        // Update the current number of this type.
        numExplorer++;
        
        addNewExplorerExtras( e1 );
        
        Explorer e2 = new Explorer( explorerIntRecycler );
        
        // Explorers must be added to the explorer set
        explorerMap.put( e2, e2 );
        
        addNewUnit( e2, startLoc );
        
        // Update the current number of this type.
        numExplorer++;
        
        addNewExplorerExtras( e2 );
        
        Colonist c = new Colonist( colonistIntRecycler );
        
        colonistMap.put( c, c );
        
        addNewUnit( c, startLoc );
        
        // Update the current number of this type.
        numColonist++;
        
        addNewColonistExtras( c );
        
        Base b = new Base( baseIntRecycler );
        
        // Bases need to be added to the structureToBase map.
        structureToBase.put( b, b );
        baseMap.put( b, b );
        
        addNewStructure( b, startLoc );
        
        // Update the current number of this type.
        numBase++;
        
        // Build the first base up.
        b.addBricks( 100 );
        
        addNewBaseExtras( b );
    }
    
    // Following methods used by build and train methods to add new
    // units/structures.
    
    /**
     * Adds a new base at the specified location.
     * 
     * @author Christopher Dudley
     * 
     * @param l
     *            the location at which to add the new base.
     */
    private void addNewBase( Location l )
    {
        // Make sure you can build a base at this location.
        if ( !canBuildStructureHere( l, numBase ) ) return;
        
        Base b = new Base( baseIntRecycler );
        
        // Bases need to be added to the structureToBase map.
        structureToBase.put( b, b );
        baseMap.put( b, b );
        
        addNewStructure( b, l );
        
        // Update the current number of this type.
        numBase++;
        
        addNewBaseExtras( b );
    }
    
    /**
     * Does extra subclass-specific things with adding a new base.
     * 
     * @author Christopher Dudley
     * 
     * @param b
     *            the base being added.
     */
    protected abstract void addNewBaseExtras( Base b );
    
    /**
     * Adds a new tower at the specified location.
     * 
     * @author Christopher Dudley
     * 
     * @param l
     *            the location at which to add the new tower.
     */
    private void addNewTower( Location l )
    {
        // Make sure you can build a tower at this location.
        if ( !canBuildStructureHere( l, numTower ) ) return;
        
        Tower t = new Tower( towerIntRecycler );
        
        towerMap.put( t, t );
        
        addNewStructure( t, l );
        
        addNewTowerExtras( t );
    }
    
    /**
     * Does extra subclass-specific things when adding a tower.
     * 
     * @param t
     *            the tower being added.
     */
    protected abstract void addNewTowerExtras( Tower t );
    
    /**
     * Does stuff common to adding any type of structure.
     * 
     * @param s
     *            the structure being added.
     * @param l
     *            the location at which the structure is being added.
     */
    private void addNewStructure( Structure s, Location l )
    {
        // Add the structure at the location.
        locationToStructure.put( l, s );
        
        // Map the instance to the location
        instanceToLocation.put( s, l );
        
        // Map the instance as a structure
        instanceToStructure.put( s, s );
    }
    
    /**
     * Adds a new explorer at the specified location.
     * 
     * @author Christopher Dudley
     * 
     * @param l
     *            the location at which to add the new explorer.
     */
    private void addNewExplorer( Location l )
    {
        // Make sure the explorer can be trained.
        if ( !canTrainUnitHere( l, numExplorer ) ) return;
        
        Explorer e = new Explorer( explorerIntRecycler );
        
        // Explorers must be added to the explorer set
        explorerMap.put( e, e );
        addNewUnit( e, l );
        
        // Update the current number of this type.
        numExplorer++;
        
        addNewExplorerExtras( e );
    }
    
    /**
     * Does subclass-specific stuff related to adding a new explorer.
     * 
     * @param e
     *            the explorer being added.
     */
    protected abstract void addNewExplorerExtras( Explorer e );
    
    /**
     * Adds a new colonist at the specified location.
     * 
     * @param l
     *            the location at which to add the colonist.
     */
    private void addNewColonist( Location l )
    {
        // Make sure the colonist can be trained.
        if ( !canTrainUnitHere( l, numColonist ) ) return;
        
        Colonist c = new Colonist( colonistIntRecycler );
        
        colonistMap.put( c, c );
        
        addNewUnit( c, l );
        
        // Update the current number of this type.
        numColonist++;
        
        addNewColonistExtras( c );
    }
    
    /**
     * Does subclass-specific stuff related to adding a new colonist.
     * 
     * @param c
     *            the colonist being added.
     */
    protected abstract void addNewColonistExtras( Colonist c );
    
    /**
     * Adds a new melee unit at the specified location.
     * 
     * @param l
     *            the location at which to add the new melee unit.
     */
    private void addNewMelee( Location l )
    {
        // Make sure the melee unit can be trained.
        if ( !canTrainUnitHere( l, numMelee ) ) return;
        
        Melee m = new Melee( meleeIntRecycler );
        
        meleeMap.put( m, m );
        
        addNewUnit( m, l );
        
        // Update the current number of this type.
        numMelee++;
        
        addNewMeleeExtras( m );
    }
    
    /**
     * Does subclass-specific stuff related to adding a new melee unit.
     * 
     * @param m
     *            the melee unit being added.
     */
    protected abstract void addNewMeleeExtras( Melee m );
    
    /**
     * Adds a new ranged unit at the specified location.
     * 
     * @author Christopher Dudley
     * 
     * @param l
     *            the location at which to add the ranged unit.
     */
    private void addNewRanged( Location l )
    {
        // Make sure the ranged unit can be trained.
        if ( !canTrainUnitHere( l, numRanged ) ) return;
        
        Ranged r = new Ranged( rangedIntRecycler );
        
        rangedMap.put( r, r );
        
        addNewUnit( r, l );
        
        // Update the current number of this type.
        numRanged++;
        
        addNewRangedExtras( r );
    }
    
    /**
     * Does subclass-specific stuff related to adding a new ranged unit.
     * 
     * @author Christopher Dudley
     * 
     * @param r
     *            the ranged unit being added.
     */
    protected abstract void addNewRangedExtras( Ranged r );
    
    /**
     * Does stuff common to adding any type of unit.
     * 
     * @author Christopher dudley
     * 
     * @param u
     *            the unit being added.
     * @param l
     *            the location at which the unit is being added.
     */
    private void addNewUnit( Unit u, Location l )
    {
        List< Unit > unitsAtLoc = locationToUnits.get( l );
        
        if ( unitsAtLoc == null ) unitsAtLoc = new ArrayList< Unit >();
        
        // When created, units are in standby mode.
        standbySet.add( u );
        
        // Add the unit to the specified location.
        instanceToLocation.put( u, l );
        
        // Allow for lookup of ModelInstance type to Unit type.
        instanceToUnit.put( u, u );
        
        unitsAtLoc.add( u );
        locationToUnits.put( l, unitsAtLoc );
    }
    
    /**
     * Determines whether or not a structure can be built at a certain location
     * given the current number of that structure type and the status of the
     * location the structure could be built at.
     * 
     * @author Christopher Dudley
     * 
     * @param l
     *            the location being asked about.
     * @param curNumStruct
     *            the current number of the type of structure going to be built.
     * 
     * @return true if the structure can be built there, false otherwise.
     */
    private boolean canBuildStructureHere( Location l, int curNumStruct )
    {
        return (curNumStruct < typeCap) // Make sure type cap not exceeded
                && (instanceToStructure.size() < totalStructCap) // Make sure
                                                                 // total cap
                                                                 // not exceeded
                && ownsLocation( l ) // Make sure location owned
                && (locationToStructure.get( l ) == null); // Make sure no other
                                                           // struct there
    }
    
    /**
     * Determines whether or not a unit can be trained at a certain location
     * given the current number of that unit type and the status of the location
     * the unit would be trained at.
     * 
     * @author Christopher Dudley
     * 
     * @param l
     *            the location at which the unit would be trained.
     * @param curNumUnit
     *            the current number of the type of unit going to be trained.
     * 
     * @return true if the unit can be trained there, false otherwise.
     */
    private boolean canTrainUnitHere( Location l, int curNumUnit )
    {
        return (curNumUnit < typeCap) // Make sure type cap not exceeded
                && (instanceToUnit.size() < totalUnitCap) // Make sure total cap
                                                          // not exceeded
                && ownsLocation( l ); // Make sure location owned
    }
    
    // It's not part of the system!
    public void throwItOnTheGround()
    {
        new ThrowItOnTheGround().cuzMyDadIsNotA( this );
    }
    
    // +harvest(instance: ModelInstance): void
    
    /**
     * Removes 10 resourceCount per colonist from location of modelInstance and
     * adds to player
     * 
     * @author Ryan S
     * @editor alex kagioglu
     */
    void harvest( ModelInstance instance )
    {
        RallyPoint rp = instanceToRallyPoint.get( instance );
        
        for ( Unit u : rp.getBattleGroup() )
        {
            if ( colonistMap.containsKey( u ) )
            {
                Location harvestLoc = instanceToLocation.get( u );
                Tile harvestTile = map.getTile( harvestLoc.x, harvestLoc.y );
                ResourceType resource = harvestTile.getResource();
                
                int tileAmount = harvestTile.getResourceCount();
                int amount = Math.min( tileAmount, 10 ); // hard coded
                
                harvestTile.subtractResource( amount );
                this.stock.put( resource, amount + this.stock.get( resource ) );
            }
        }
        
        instance.popCommand();
    }
    
    // +powerUp(instance: ModelInstance): void
    /**
     * Power up target instance
     * 
     * @deprecated
     * @author Ryan S
     */
    void powerUp( ModelInstance instance )
    {
        if ( !instance.isPowered() ) instance.powerUp();
    }
    
    // +powerDown(instance: ModelInstance): void
    /**
     * Power down target instance
     * 
     * @deprecated
     * @author Ryan S
     */
    void powerDown( ModelInstance instance )
    {
        if ( instance.isPowered() ) instance.powerDown();
    }
    
    // +disband(instance: ModelInstance): void
    /**
     * Disband target RallyPoint, add units to standby
     * 
     * @author Ryan S
     */
    void disband( ModelInstance instance )
    { /*
       * -standbySet: Set<Unit> -instanceToRallyPoint: Map<ModelInstance,
       * RallyPoint> -unitToRallyPoint: Map<Unit, RallyPoint>
       * -locationToRallyPoints: Map<Location, List<RallyPoint> >
       * -idToRallyPoint: Map<Integer, RallyPoint>
       */
        
        RallyPoint rp = instanceToRallyPoint.remove( instance );
        Location loc = instanceToLocation.remove( instance );
        locationToRallyPoint.get( loc ).remove( rp );
        
        idToRallyPoint.remove( rp.getID() );
        
        Unit[] toRemove = rp.getEveryone();
        
        for ( int i = 0; i < toRemove.length; i++ )
        {
            standbySet.add( toRemove[ i ] );
            unitToRallyPoint.remove( toRemove[ i ] );
            
            if ( explorerMap.containsKey( toRemove[ i ] ) )
                explorerLeaveArmy( explorerMap.get( toRemove[ i ] ), rp );
            else if ( colonistMap.containsKey( toRemove[ i ] ) )
                colonistLeaveArmy( colonistMap.get( toRemove[ i ] ), rp );
            else if ( meleeMap.containsKey( toRemove[ i ] ) )
                meleeLeaveArmy( meleeMap.get( toRemove[ i ] ), rp );
            else if ( rangedMap.containsKey( toRemove[ i ] ) )
                rangedLeaveArmy( rangedMap.get( toRemove[ i ] ), rp );
        }
        
        rp.disband();
        disbandExtras( rp );
    }
    
    /**
     * Allows subclasses to do subclass-specific operations when a colonist
     * leaves an army.
     * 
     * @param m
     *            the colonist leaving the army.
     * @param rp
     *            the rally point of the army the colonist is leaving.
     */
    protected abstract void colonistLeaveArmy( Colonist e, RallyPoint rp );
    
    /**
     * Allows subclasses to do subclass-specific operations when an explorer
     * leaves an army.
     * 
     * @param m
     *            the explorer leaving the army.
     * @param rp
     *            the rally point of the army the explorer is leaving.
     */
    protected abstract void explorerLeaveArmy( Explorer e, RallyPoint rp );
    
    /**
     * Allows subclasses to do subclass-specific operations when a melee unit
     * leaves an army.
     * 
     * @param m
     *            the melee unit leaving the army.
     * @param rp
     *            the rally point of the army the melee unit is leaving.
     */
    protected abstract void meleeLeaveArmy( Melee m, RallyPoint rp );
    
    /**
     * Allows subclasses to do subclass-specific operations when a ranged unit
     * leaves an army.
     * 
     * @param m
     *            the ranged unit leaving the army.
     * @param rp
     *            the rally point of the army the ranged unit is leaving.
     */
    protected abstract void rangedLeaveArmy( Ranged r, RallyPoint rp );
    
    /**
     * Allows subclasses to do subclass-specific operations when a rally point
     * is disbanded.
     * 
     * @param rp
     *            the rally point being disbanded.
     */
    protected abstract void disbandExtras( RallyPoint rp );
    
    // +decommission(instance: ModelInstance): void
    /**
     * Destroy target instance
     * 
     * @author Ryan S
     */
    void decommission( ModelInstance instance )
    {
        
        if ( instanceToUnit.containsKey( instance ) )
        {
            Unit u = instanceToUnit.remove( instance );
            decommission( u );
        }
        
        else if ( instanceToStructure.containsKey( instance ) )
        {
            Structure s = instanceToStructure.remove( instance );
            decommission( s );
        }
        
        else if ( instanceToRallyPoint.containsKey( instance ) )
        {
            RallyPoint rp = instanceToRallyPoint.remove( instance );
            Location loc = instanceToLocation.remove( instance );
            Unit toRemove[] = rp.getEveryone();
            
            for ( int i = 0; i < toRemove.length; i++ )
                this.decommission( toRemove[ i ] );
            
            locationToRallyPoint.get( loc ).remove( rp );
            idToRallyPoint.remove( rp.getID() );
            
            decommRallyPointExtras( rp );
        }
        
        instance.decommission();
    }
    
    /**
     * Allows subclasses to do subclass-specific things when a really point is
     * decommissioned.
     * 
     * @param rp
     *            the rally point being decommissioned. @
     */
    protected abstract void decommRallyPointExtras( RallyPoint rp );
    
    /**
     * Removes a unit from the various sets and maps of player, thereby erasing
     * it from existance.
     * 
     * @author Ryan S.
     * @author Christopher Dudley
     * 
     * @param u
     *            the unit to decommission.
     */
    private void decommission( Unit u )
    {
        if ( standbySet.contains( u ) ) standbySet.remove( u );
        Location loc = instanceToLocation.remove( (ModelInstance) u );
        locationToUnits.get( loc ).remove( u );
        
        if ( explorerMap.containsKey( u ) )
        {
            Explorer e = explorerMap.remove( u );
            numExplorer--;
            decommExplorerExtras( e );
        }
        else if ( colonistMap.containsKey( u ) )
        {
            Colonist c = colonistMap.remove( u );
            numColonist--;
            decommColonistExtras( c );
        }
        else if ( meleeMap.containsKey( u ) )
        {
            Melee m = meleeMap.remove( u );
            numMelee--;
            decommMeleeExtras( m );
        }
        else if ( rangedMap.containsKey( u ) )
        {
            Ranged r = rangedMap.remove( u );
            numRanged--;
            decommRangedExtras( r );
        }
        else
        {
            // Shouldn't happen.
            this.throwItOnTheGround();
        }
        
        if ( unitToRallyPoint.containsKey( u ) ) unitToRallyPoint.remove( u );
        
        u.decommission();
    }
    
    /**
     * Allows subclasses to do subclass-specific operations when a colonist is
     * decommissioned.
     * 
     * @param c
     *            the colonist that was decommissioned.
     */
    protected abstract void decommColonistExtras( Colonist c );
    
    /**
     * Allows subclasses to do subclass-specific operations when a colonist is
     * decommissioned.
     * 
     * @param e
     *            the explorer that was decommissioned.
     */
    protected abstract void decommExplorerExtras( Explorer e );
    
    /**
     * Allows subclasses to do subclass-specific operations when a colonist is
     * decommissioned.
     * 
     * @param m
     *            the melee unit that was decomissioned.
     */
    protected abstract void decommMeleeExtras( Melee m );
    
    /**
     * Allows subclasses to do subclass-specific operations when a colonist is
     * decomissioned.
     * 
     * @param r
     *            the ranged unit that was decommissoned.
     */
    protected abstract void decommRangedExtras( Ranged r );
    
    /**
     * Allows subclasses to do subclass-specific operations when a base is
     * decommissioned.
     * 
     * @param b
     *            the base that was decommissioned.
     */
    protected abstract void decommBaseExtras( Base b );
    
    /**
     * Allow subclasses to do subclass-specific operations when a tower is
     * decommissioned.
     * 
     * @param t
     *            the tower that was decommissioned.
     */
    protected abstract void decommTowerExtras( Tower t );
    
    /**
     * Removes a structure from the various sets and maps of player, thereby
     * erasing it from existance.
     * 
     * @author Ryan S.
     * @author Christopher Dudley
     * 
     * @param s
     *            the structure to decommission.
     */
    private void decommission( Structure s )
    {
        Location loc = instanceToLocation.remove( (ModelInstance) s );
        locationToStructure.remove( loc );
        
        if ( baseMap.containsKey( s ) )
        {
            baseMap.remove( s );
            numBase--;
        }
        else if ( towerMap.containsKey( s ) )
        {
            towerMap.remove( s );
            numTower--;
        }
        else
        {
            // Shouldn't happen
            this.throwItOnTheGround();
        }
    }
    
    // +createRallyPoint(rallypoint: int): void
    /**
     * create new Rally Point bound to keypad number
     * 
     * @author Ryan S
     */
    public void createRallyPoint( int rallyPoint )
    {
        if ( !idToRallyPoint.containsKey( rallyPoint ) )
        {
            RallyPoint newRP = new RallyPoint( rallyPoint );
            idToRallyPoint.put( rallyPoint, newRP );
            instanceToLocation.put( newRP, startingLocation );
            
            List< RallyPoint > l = locationToRallyPoint.get( startingLocation );
            
            if ( l == null ) l = new LinkedList< RallyPoint >();
            
            l.add( newRP );
            
            locationToRallyPoint.put( startingLocation, l );
            instanceToRallyPoint.put( newRP, newRP );
            createRallyPointExtras( newRP );
        }
    }
    
    /**
     * Allows subclasses to add subclass-specific operations when a rally point
     * is created.
     * 
     * @param rp
     *            the rally point being created.
     */
    protected abstract void createRallyPointExtras( RallyPoint rp );
    
    // +leaveArmy(instance: ModelInstance): void
    /**
     * remove Unit from rallyPoint --> add to standby
     * 
     * @author Ryan S
     */
    public void leaveArmy( ModelInstance instance )
    {
        Unit removedUnit = instanceToUnit.get( instance );
        RallyPoint rp = unitToRallyPoint.remove( removedUnit );
        
        rp.removeUnit( removedUnit );
        standbySet.add( removedUnit );
    }
    
    // +buildTower(builder: ModelInstance): void
    /**
     * 
     * @author Ryan S
     */
    public void buildTower( ModelInstance builder )
    {
        Location location = this.instanceToLocation.get( builder );
        RallyPoint rpBuilder = instanceToRallyPoint.get( builder );
        if ( this.locationToStructure.containsKey( location ) )
        {
            buildExistingStructure( rpBuilder, location );
        }
        else
        {
            if ( getNumColInRP( rpBuilder ) > 0 )
                addNewTower( location );
            else
                builder.popCommand();
        }
    }
    
    // +buildBase(base: Base, builder: ModelInstance): void
    /**
     * 
     * @author Ryan S
     */
    public void buildBase( ModelInstance builder )
    {
        Location location = this.instanceToLocation.get( builder );
        RallyPoint rpBuilder = instanceToRallyPoint.get( builder );
        if ( this.locationToStructure.containsKey( location ) )
        {
            buildExistingStructure( rpBuilder, location );
        }
        else
        {
            if ( getNumColInRP( rpBuilder ) > 0 )
                addNewBase( location );
            else
                builder.popCommand();
        }
    }
    
    /**
     * Adds more bricks to an existing structure that has not yet been finished.
     * 
     * @param builder
     *            the rally point that is building the building.
     * @param location
     *            the location at which the building is being built.
     */
    private void buildExistingStructure( RallyPoint builder, Location location )
    {
        int num_colonists = getNumColInRP( builder );
        if ( num_colonists == 0 )
        {
            builder.popCommand(); // no colonists in the army
        }
        else
        {
            Structure constructure = this.locationToStructure.get( location );
            constructure.addBricks( num_colonists );
            if ( constructure.isCompleted() ) builder.popCommand();
        }
    }
    
    private int getNumColInRP( RallyPoint rp )
    {
        int numColonists = 0;
        
        for ( Unit u : rp.getBattleGroup() )
        {
            if ( colonistMap.containsKey( u ) ) numColonists++;
        }
        
        return numColonists;
    }
    
    /*
     * Deviation: build() command replaced by separate buildBase and buildTower
     * methods.
     */

    // +trainMelee( trainer: ModelInstance): void
    /**
     * @author ryan
     */
    public void trainMelee( ModelInstance trainer )
    {
        trainer.popCommand();
        addNewMelee( instanceToLocation.get( trainer ) );
    }
    
    // +trainRanged(trainer: ModelInstance): void
    /**
     * @author ryan
     */
    public void trainRanged( ModelInstance trainer )
    {
        trainer.popCommand();
        addNewRanged( instanceToLocation.get( trainer ) );
    }
    
    // +trainColonist(trainer: ModelInstance): void
    /**
     * @author ryan
     */
    public void trainColonist( ModelInstance trainer )
    {
        trainer.popCommand();
        addNewColonist( instanceToLocation.get( trainer ) );
    }
    
    // +trainExplorer(trainer: ModelInstance): void
    /**
     * 
     * @author alex kagioglu
     * @author Christopher Dudley
     */
    public void trainExplorer( ModelInstance trainer )
    {
        trainer.popCommand();
        addNewExplorer( instanceToLocation.get( trainer ) );
    }
    
    /*
     * Deviation: train() method removed, not needed.
     */

    // +heal(instance: ModelInstance): void
    /**
     * 
     * @author Ryan S
     */
    public void heal( ModelInstance instance )
    {
        Structure s = instanceToStructure.get( instance );
        Base b = baseMap.get( s );
        
        Location loc = instanceToLocation.get( instance );
        boolean everyoneIsDrunk = true;
        
        if ( locationToUnits.containsKey( loc ) )
        {
            List< Unit > healedUnits = locationToUnits.get( loc );
            for ( Unit u : healedUnits )
            {
                u.getHealed( b.getHeal() );
                everyoneIsDrunk = everyoneIsDrunk && u.isFullyHealth();
            }
        }
        
        if ( locationToRallyPoint.containsKey( loc ) )
        {
            List< RallyPoint > healedArmies = locationToRallyPoint.get( loc );
            for ( RallyPoint rp : healedArmies )
            {
                LinkedList< Unit > getHealed = rp.getBattleGroup();
                for ( Unit u : getHealed )
                {
                    u.getHealed( b.getHeal() );
                    everyoneIsDrunk = everyoneIsDrunk && u.isFullyHealth();
                }
            }
        }
        if ( everyoneIsDrunk ) instance.popCommand();
    }
    
    // +joinRallyPoint(instance: ModelInstance, id: int): void
    /**
     * 
     * @author Ryan S
     */
    public void joinRallyPoint( ModelInstance instance, int id )
    {
        RallyPoint rp = idToRallyPoint.get( id );
        Unit u = instanceToUnit.get( instance );
        
        if ( rp != null && !(unitToRallyPoint.get( u ) == rp) )
        {
            if ( unitToRallyPoint.containsKey( u ) )
            {
                RallyPoint oldRallyPoint = unitToRallyPoint.get( u );
                oldRallyPoint.removeUnit( u );
            }
            else
            {
                standbySet.remove( u );
                
                if ( meleeMap.containsKey( u ) )
                    decommMeleeExtras( meleeMap.get( u ) );
                else if ( rangedMap.containsKey( u ) )
                    decommRangedExtras( rangedMap.get( u ) );
                else if ( colonistMap.containsKey( u ) )
                    decommColonistExtras( colonistMap.get( u ) );
                else if ( explorerMap.containsKey( u ) )
                    decommExplorerExtras( explorerMap.get( u ) );
            }
            
            rp.addUnit( u );
            
            unitToRallyPoint.put( u, rp );
            
            if ( instanceToLocation.get( u ).equals( instanceToLocation.get( rp ) ) )
            {
                rp.transferUnit( u );
                u.popCommand();
            }
        }
        else if ( rp != null && (unitToRallyPoint.get( u ) == rp) )
        {
            if ( rp.getBattleGroup().contains( u ) )
            {
                System.out.println( "Unit is in the battlegroup already, silly!" );
                u.popCommand();
            }
            else if ( instanceToLocation.get( u ).equals( instanceToLocation.get( rp ) ) )
            {
                rp.transferUnit( u );
                u.popCommand();
            }
            else
            {
                System.out.println( "Moving towards rally point" );
                moveTowardsRallyPoint( u, rp );
            }
        }
    }
    
    /**
     * Tries to move a unit in a rally point's reinforcements closer to the
     * rally point.
     * 
     * @param u
     *            the unit being moved.
     * @param rp
     *            the target rally point.
     */
    private void moveTowardsRallyPoint( Unit u, RallyPoint rp )
    {
        Location start = instanceToLocation.get( u );
        Location end = instanceToLocation.get( rp );
        
        Direction[] directions = Direction.values();
        double[] dirPriority = new double[ directions.length ];
        
        for ( int i = 0; i < directions.length; i++ )
        {
            int newX = start.x + directions[ i ].x;
            int newY = start.y + directions[ i ].y;
            
            dirPriority[ i ] = distance( end.x, end.y, newX, newY );
        }
        
        for ( int i = 0; i < directions.length - 1; i++ )
        {
            double curMin = dirPriority[ i ];
            int minIndex = i;
            
            for ( int j = i + 1; j < directions.length; j++ )
            {
                if ( dirPriority[ j ] < curMin )
                {
                    curMin = dirPriority[ j ];
                    minIndex = j;
                }
            }
            
            if ( minIndex != i )
            {
                double tempPriority = dirPriority[ i ];
                Direction tempDir = directions[ i ];
                
                dirPriority[ i ] = curMin;
                directions[ i ] = directions[ minIndex ];
                
                dirPriority[ minIndex ] = tempPriority;
                directions[ minIndex ] = tempDir;
            }
        }
        
        for ( int i = 0; i < directions.length; i++ )
        {
            Location newLoc = new Location( start.x + directions[ i ].x, start.y
                    + directions[ i ].y );
            
            if ( canMove( newLoc ) )
            {
                locationToUnits.get( start ).remove( u );
                instanceToLocation.remove( u );
                
                instanceToLocation.put( u, newLoc );
                List< Unit > unitsAtLoc = locationToUnits.get( newLoc );
                
                if ( unitsAtLoc == null ) unitsAtLoc = new ArrayList< Unit >();
                
                unitsAtLoc.add( u );
                locationToUnits.put( newLoc, unitsAtLoc );
                Tile zeTile = map.getTile( newLoc.x, newLoc.y );
                
                if ( zeTile.hasItem() )
                {
                    Item zeItem = map.getTile( newLoc.x, newLoc.y ).getItem();
                    
                    zeItem.accept( this, new ItemVisitorClass(), u );
                    
                    zeTile.removeItem();
                }
                
                if ( zeTile.getTerrainType() == TerrainType.OUTER_SPACE )
                    decommission( (ModelInstance) u );
                
                i = directions.length;
            }
        }
    }
    
    private double distance( int endX, int endY, int startX, int startY )
    {
        double temp1 = Math.pow( (endX - startX), 2 );
        double temp2 = Math.pow( endY - startY, 2 );
        return Math.sqrt( temp1 + temp2 );
    }
    
    /**
     * Determines whether or not something can move from one location to
     * another.
     * 
     * @param end
     *            the location being queried about.
     * 
     * @return whether or not the unit can move to the specified location.
     */
    private boolean canMove( Location loc )
    {
        boolean movePossible = false;
        
        if ( loc.x >= 0 && loc.x < map.getWidth() && loc.y >= 0 && loc.y < map.getHeight() )
        {
            Tile tileAtLoc = map.getTile( loc.x, loc.y );
            
            if ( tileAtLoc.getTerrainType() != TerrainType.PLATEAU )
            {
                // Can move to this terrain type.
                
                if ( (tileAtLoc.hasItem() && !tileAtLoc.getItem().isObstacle())
                        || !tileAtLoc.hasItem() )
                ;
                {
                    // No obstacle! Yay!
                    
                    if ( !enemy.ownsLocation( loc ) )
                    {
                        // Tile is ok to move to
                        movePossible = true;
                    }
                }
            }
        }
        
        return movePossible;
    }
    
    // +attack(instance: ModelInstance, direction: DirectionEnum): void
    public void attack( ModelInstance instance, Direction direction )
    {
        this.throwItOnTheGround();
    }
    
    // +defend(instance: ModelInstance, direction: DirectionEnum): void
    public void defend( ModelInstance instance, Direction direction )
    {
        this.throwItOnTheGround();
    }
    
    // +move(instance: ModelInstance, command: Move): void
    public void move( ModelInstance instance, Move command )
    {
        Direction dir = null;
        
        if ( command.hasNext() )
            dir = command.peek();
        else
            instance.popCommand();
        
        RallyPoint rp = instanceToRallyPoint.get( instance );
        
        if ( (dir != null) && rp.step( dir ) )
        {
            command.pop();
            tryToMoveRP( rp, dir );
        }
    }
    
    /**
     * Tries to move the specified rally point in the specified direction.
     * 
     * @param rp
     *            the rally point to attempt to move.
     * @param dir
     *            the direction in which to attempt to move.
     */
    private void tryToMoveRP( RallyPoint rp, Direction dir )
    {
        Location curLoc = instanceToLocation.get( rp );
        
        int nextX = curLoc.x + dir.x;
        int nextY = curLoc.y + dir.y;
        Location newLoc = new Location( nextX, nextY );
        
        if ( canMove( newLoc ) ) moveRP( rp, newLoc );
    }
    
    /*
     * TODO: Add checks to activate one-shot items, explore terrain, etc.
     */
    private void moveRP( RallyPoint rp, Location toMove )
    {
        locationToRallyPoint.get( instanceToLocation.get( rp ) ).remove( rp );
        instanceToLocation.remove( rp );
        
        List< RallyPoint > rpAtLoc = locationToRallyPoint.get( toMove );
        
        if ( rpAtLoc == null ) rpAtLoc = new ArrayList< RallyPoint >();
        
        rpAtLoc.add( rp );
        locationToRallyPoint.put( toMove, rpAtLoc );
        instanceToLocation.put( rp, toMove );
        
        for ( Unit u : rp.getBattleGroup() )
        {
            locationToUnits.get( instanceToLocation.get( u ) ).remove( u );
            instanceToLocation.remove( u );
            
            List< Unit > unitsAtLoc = locationToUnits.get( toMove );
            
            if ( unitsAtLoc == null ) unitsAtLoc = new ArrayList< Unit >();
            
            unitsAtLoc.add( u );
            locationToUnits.put( toMove, unitsAtLoc );
            instanceToLocation.put( u, toMove );
        }
        
        Tile zeTile = map.getTile( toMove.x, toMove.y );
        
        if ( zeTile.hasItem() )
        {
            Item zeItem = zeTile.getItem();
            
            // Should never be true in this case anyways as the units wouldn't
            // have been allowed
            // to move here in the first place, but check just in case.
            if ( !zeItem.isObstacle() )
            {
                zeItem.accept( this, new ItemVisitorClass(), rp );
                
                zeTile.removeItem();
            }
        }
        
        if ( zeTile.getTerrainType() == TerrainType.OUTER_SPACE )
            decommission( (ModelInstance) rp );
    }
    
    // +ownsLocation(location: Location): boolean
    /**
     * 
     * @author alex kagioglu
     */
    public boolean ownsLocation( Location location )
    {
        return this.locationToUnits.containsKey( location )
                || this.locationToRallyPoint.containsKey( location )
                || this.locationToStructure.containsKey( location );
    }
    
    // +hasLostTheGame(): boolean
    // YOU JUST LOST THE GAME!!!
    /**
     * 
     * @author alex kagioglu
     */
    public boolean hasLostTheGame()
    {
        return instanceToStructure.isEmpty();
    }
    
    // +onTick(enemy: Player): void
    // i deviated from the uml. player now has attributes of enemy and map.
    /**
     *
     */
    public void onTick()
    {
        // standy iteration
        HashSet< Unit > standbyCopy = new HashSet< Unit >();
        for ( Unit u : standbySet )
        {
            standbyCopy.add( u );
        }
        
        for ( Unit u : standbyCopy )
        {
            Command curCommand = u.onTick( this );
            if ( curCommand != null ) curCommand.run( this, u );
        }
        standbyCopy = null;
        
        // rallypoint iteration. alex kagioglu
        HashSet< RallyPoint > rallypCopy = new HashSet< RallyPoint >();
        for ( Map.Entry< ModelInstance, RallyPoint > entry : this.instanceToRallyPoint.entrySet() )
        {
            rallypCopy.add( entry.getValue() );
        }
        
        for ( RallyPoint rp : rallypCopy )
        {
            Unit[] reservesCopy = new Unit[ rp.getReserves().length ];
            int i = 0;
            
            for ( Unit u : rp.getReserves() )
            {
                reservesCopy[ i ] = u;
                i++;
            }
            
            for ( Unit u : reservesCopy )
            {
                Command zeCommand = u.onTick( this );
                zeCommand.run( this, u );
            }
            
            reservesCopy = null;
            
            Command curCommand = rp.onTick( this );
            curCommand.run( this, rp );
        }
        rallypCopy = null;
        
        // Command Structures Chris Woolfe
        HashSet< Structure > StructuresCopy = new HashSet< Structure >();
        for ( Map.Entry< ModelInstance, Structure > entry : this.instanceToStructure.entrySet() )
        {
            StructuresCopy.add( entry.getValue() );
        }
        
        for ( Structure s : StructuresCopy )
        {
            Command curCommand = s.onTick( this );
            curCommand.run( this, s );
        }
        StructuresCopy = null;
        
        // explorer iteration alex kagioglu
        HashSet< Explorer > explorerCopy = new HashSet< Explorer >();
        for ( Map.Entry< Unit, Explorer > entry : this.explorerMap.entrySet() )
        {
            explorerCopy.add( entry.getValue() );
        }
        
        Location[] locs;
        for ( Explorer explo : explorerCopy )
        {
            locs = explo.getExploredArray( this.instanceToLocation.get( explo ) );
            for ( Location l : locs )
            {
                this.exploreFully( l );
            }
        }
        explorerCopy = null;
        
        // Do exploration
        for ( Map.Entry< Unit, Melee > m : meleeMap.entrySet() )
        {
            Melee curMelee = m.getValue();
            
            Location curLocation = instanceToLocation.get( curMelee );
            
            explorePartially( curLocation );
        }
        for ( Map.Entry< Unit, Ranged > r : rangedMap.entrySet() )
        {
            Ranged curRanged = r.getValue();
            
            Location curLocation = instanceToLocation.get( curRanged );
            
            explorePartially( curLocation );
        }
        for ( Map.Entry< Unit, Colonist > c : colonistMap.entrySet() )
        {
            // Also do harvesting!
            Colonist curColonist = c.getValue();
            
            Location curLocation = instanceToLocation.get( curColonist );
            
            explorePartially( curLocation );
        }
    }
    
    /**
     * Explores the location an explorer was at
     * 
     * @param location
     *            the location being explored.
     * @param whether
     *            the person doing the exploring was an explorer or not.
     */
    private void explorePartially( Location location )
    {
        if ( this.visibleMap.containsKey( location ) )
        {
            if ( this.visibleMap.get( location ) != DecalType.EXPLORED_FULLY_NO_DECAL
                    && this.visibleMap.get( location ) != DecalType.DANGER_ZONE )
            {
                this.visibleMap.put( location, DecalType.EXPLORED_BASIC );
            }
        }
        else
        {
            this.visibleMap.put( location, DecalType.EXPLORED_BASIC );
        }
    }
    
    /**
     * Explores the location an explorer was at
     * 
     * @param location
     *            the location being explored.
     * @param whether
     *            the person doing the exploring was an explorer or not.
     */
    private void exploreFully( Location location )
    {
        try
        {
            this.visibleMap.put( location, DecalType.EXPLORED_FULLY_NO_DECAL );
            if ( this.map.getTile( location.x, location.y ).getTerrainType() == TerrainType.OUTER_SPACE )
            {
                this.visibleMap.put( location, DecalType.DANGER_ZONE );
            }
        }
        catch ( ArrayIndexOutOfBoundsException e )
        {
            // the tile was off the map
        }
    }
    
    // i deviated from the uml. player needs a way to set it's enemy.
    /**
     * 
     * @author alex kagioglu
     */
    public void setEnemy( Player enemy )
    {
        this.enemy = enemy;
    }
    
    /**
     * 
     * @author alex kagioglu
     */
    private boolean sufficientResources( Map< ResourceType, Integer > map )
    {
        for ( ResourceType resource : map.keySet() )
        {
            if ( this.stock.get( resource ) < map.get( resource ) ) return false;
        }
        return true;
    }
    
    /**
     * 
     * @author alex kagioglu
     */
    private void subtractResources( Map< ResourceType, Integer > map )
    {
        for ( ResourceType resource : map.keySet() )
        {
            int difference = this.stock.get( resource ) - map.get( resource );
            this.stock.put( resource, difference );
        }
    }
    
    public void addResources( Map< ResourceType, Integer > map )
    {
        for ( ResourceType resource : map.keySet() )
        {
            int newAmount = this.stock.get( resource ) + map.get( resource );
            this.stock.put( resource, newAmount );
        }
    }
    
    // Deviation from the UML(?): Need a way to get a list of units at a certain
    // location
    /**
     * 
     * @author Adam Szpakowski
     */
    public List< Unit > getUnitsAt( Location l )
    {
        return locationToUnits.get( l );
    }
    
    // Deviation from the UML(?): Need a way to get a structure at a certain
    // location
    /**
     * 
     * @author Adam Szpakowski
     */
    public Structure getStructureAt( Location l )
    {
        return locationToStructure.get( l );
    }
    
    // Deviation from the UML(?): Need a way to get a decal at a certain
    // location
    /**
     * 
     * @author Adam Szpakowski
     */
    public DecalType getDecalAt( Location l )
    {
        if ( visibleMap.get( l ) == null ) return DecalType.UNEXPLORED;
        return visibleMap.get( l );
    }
    
    /**
     * Returns the location of the selected ModelInstance as a Point.
     * 
     * @param mInst
     *            the model instance for which to check its location.
     * 
     * @return the location of the instance if it exists, null otherwise.
     */
    protected Location getInstLocation( ModelInstance mInst )
    {
        Location loc = null;
        
        if ( instanceToLocation.containsKey( mInst ) ) loc = instanceToLocation.get( mInst );
        
        return loc;
    }
    
    /**
     * DEVIATION FROM UML: Needed for ScienceTile to get stuff for View's
     * perusal Returns a list of RallyPoints at a given location
     * 
     * @return the list of RallyPoints at the location
     * @author Adam Szpakowski
     */
    public int[] getRallyPointIDsAt( Location l )
    {
        List< RallyPoint > list = locationToRallyPoint.get( l );
        
        if ( list == null ) return new int[ 0 ];
        
        int[] IDs = new int[ list.size() ];
        for ( int i = 0; i < IDs.length; i++ )
        {
            IDs[ i ] = list.get( i ).getID();
        }
        
        return IDs;
    }
    
    /**
     * 
     */
    public int getResourceCount( ResourceType r )
    {
        return (stock.get( r )).intValue();
    }
    
    /**
     * Returns an unmodifiable view of the set of bases of the player.
     * 
     * @author Christopher Dudley
     * 
     * @return a view of the set of bases owned by this player.
     */
    protected Map< Structure, Base > getBaseMap()
    {
        Map< Structure, Base > unmodBaseMap = Collections.unmodifiableMap( baseMap );
        return unmodBaseMap;
    }
    
    /**
     * Returns an unmodifiable view of the set of colonists under the player's
     * command.
     * 
     * @author Christopher Dudley
     * 
     * @return a view of the set of player's colonists.
     */
    protected Map< Unit, Colonist > getColonistMap()
    {
        Map< Unit, Colonist > unmodColonistMap = Collections.unmodifiableMap( colonistMap );
        return unmodColonistMap;
    }
    
    /**
     * Returns an unmodifiable view of the set of explorers under the player's
     * command.
     * 
     * @author Christopher Dudley
     * 
     * @return a view of the set of the player's explorers.
     */
    protected Map< Unit, Explorer > getExplorerMap()
    {
        Map< Unit, Explorer > unmodExplorerMap = Collections.unmodifiableMap( explorerMap );
        return unmodExplorerMap;
    }
    
    /**
     * Returns an unmodifiable view of the set of melee units under the player's
     * command.
     * 
     * @author Christopher Dudley
     * 
     * @return a view of the set of the player's melee units.
     */
    protected Map< Unit, Melee > getMeleeMap()
    {
        Map< Unit, Melee > unmodMeleeMap = Collections.unmodifiableMap( meleeMap );
        return unmodMeleeMap;
    }
    
    /**
     * Returns an unmodifiable view of the set of ranged units under the
     * player's command.
     * 
     * @author Christopher Dudley
     * 
     * @return a view of the set of the player's ranged units.
     */
    protected Map< Unit, Ranged > getRangedMap()
    {
        Map< Unit, Ranged > unmodRangedMap = Collections.unmodifiableMap( rangedMap );
        return unmodRangedMap;
    }
    
    /**
     * Returns an unmodifiable view of the set of towers under the player's
     * command.
     * 
     * @author Christopher Dudley
     * 
     * @return a view of the set of the player's towers.
     */
    protected Map< Structure, Tower > getTowerMap()
    {
        Map< Structure, Tower > unmodTowerMap = Collections.unmodifiableMap( towerMap );
        return unmodTowerMap;
    }
    
    @Override
    public String toString()
    {
        return new String( "Number of Units:" + standbySet.size() );
        
    }
}
